package com.example.limit.service;

import com.example.limit.listener.ConfigRefreshListener;
import com.example.limit.properties.RateLimiterConfig;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class HybridRateLimiter implements IRateLimiter {

    // 本地限流器缓存，Guava 实现
    private final Map<String, RateLimiter> localLimiters = new ConcurrentHashMap<>();

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private RateLimiterConfig config;

    @Autowired
    private ConfigRefreshListener configRefreshListener;

    @PostConstruct
    public void init() {
        // 初始化限流器
        initLocalLimiters();
        // 注册配置热更新监听器
        configRefreshListener.addListener(this::refreshConfig);
    }

    /**
     * 本地限流 - 立即尝试获取
     */
    @Override
    public boolean tryAcquire(String module) {
        try {
            // 分布式限流优先
            if (config.isDistributedEnabled()) {
                boolean distributedPass = tryDistributedAcquire(module);
                if (!distributedPass) return false;
            }
            return tryLocalAcquire(module);
        } catch (Exception e) {
            log.error("限流异常, 开始方案降级处理: {}", module, e);
            return handleDegrade(module);
        }
    }

    /**
     * 本地限流 - 等待超时尝试获取
     */
    @Override
    public boolean tryAcquire(String module, long timeout, TimeUnit unit) {
        try {
            // 分布式限流优先
            if (config.isDistributedEnabled()) {
                boolean distributedPass = tryDistributedAcquire(module);
                if (!distributedPass) return false;
            }
            return tryLocalAcquire(module, timeout, unit);
        } catch (Exception e) {
            log.error("限流异常（带超时）, 开始方案降级处理: {}", module, e);
            return handleDegrade(module);
        }
    }

    private boolean tryLocalAcquire(String module) {
        RateLimiter limiter = localLimiters.get(module);
        if (limiter == null) return true;
        return limiter.tryAcquire();
    }

    private boolean tryLocalAcquire(String module, long timeout, TimeUnit unit) {
        RateLimiter limiter = localLimiters.get(module);
        if (limiter == null) return true;
        return limiter.tryAcquire(timeout, unit);
    }

    /**
     * 分布式限流实现（滑动窗口算法，基于 Redis ZSet）
     */
    private boolean tryDistributedAcquire(String module) {
        String key = "rate_limit:" + module;
        long now = System.currentTimeMillis();
        long windowStart = now - 1000;
        RateLimiterConfig.ModuleConfig moduleConfig = config.getModules().get(module);
        if (moduleConfig == null) return true;

        String script =
                "local key = KEYS[1] \n" +
                        "local now = tonumber(ARGV[1]) \n" +
                        "local window_start = tonumber(ARGV[2]) \n" +
                        "local limit = tonumber(ARGV[3]) \n" +
                        "local request_id = ARGV[4] \n" +
                        "redis.call('zremrangebyscore', key, 0, window_start) \n" +
                        "local current = redis.call('zcard', key) \n" +
                        "if current < limit then \n" +
                        " redis.call('zadd', key, now, request_id) \n" +
                        " redis.call('expire', key, 3) \n" +
                        " return 1 \n" +
                        "else return 0 end";

        Long result = redisTemplate.execute(
                new DefaultRedisScript<>(script, Long.class),
                Collections.singletonList(key),
                String.valueOf(now),
                String.valueOf(windowStart),
                String.valueOf(moduleConfig.getQps()),
                UUID.randomUUID().toString()
        );
        return result != null && result == 1;
    }


    /**
     * 降级策略处理
     */
    private boolean handleDegrade(String module) {
        switch (config.getDegradeStrategy()) {
            case LOCAL_ONLY:
                return tryLocalAcquire(module);
            case PASS_THROUGH:
                return true;
            default:
                return false;
        }
    }


    /**
     * 初始化本地限流器集合
     */
    private void initLocalLimiters() {
        config.getModules().forEach((name, moduleConfig) -> {
            if (moduleConfig.isEnabled()) {
                localLimiters.put(name,
                        com.google.common.util.concurrent.RateLimiter.create(moduleConfig.getQps()));
            }
        });
    }



    /**
     * 刷新配置中心配置
     */
    private void refreshConfig(RateLimiterConfig newConfig) {
        this.config = newConfig;
        localLimiters.clear();
        initLocalLimiters();
    }
}
























